/*!
//      
//      \file	k2k_Signal.c
//      
//      \author	Copyright 2010 Valentina Vuksic <vuksv1@bfh.ch>, Beat Liechti <liecb2@bfh.ch>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.
//
//      Abstract Data Type for signal handling
//      
//      \brief		CAS-EBX-03 Projektarbeit 
//      			Valentina Vuksic (VUKSV1), Beat Liechti (LIECB2)
//      			Signal ADT for the k2k (Kaffee2000) application
//      \version	1.0
//      \date		2010-04-19
//      \remark		Last Modifications:
//      \remark  	v1.0, VUKSV1/LIECB2, 2010-05-05 Initial release
//
*/

#include <stdio.h>
#include <sys/wait.h>

#include "k2k_Signal.h"

/* -- static function declarations -----------------------------------*/

static void k2k_Signal_CallbackFuncInternal(int sigNo, siginfo_t *sigInfo, void *customData);

/* -- ADT functions --------------------------------------------------*/

/*!
//
// Creates Signal ADT "object"
//
// \param sigNo si the signal type: SIGHUP, SIGINT, SIGUSR1, SIGUSR2, etc
// \param callback is the callback function (signal receiver)
// \param customData is a pointer to custom data to call the callback function with
// 
// \return pointer to the Signal ADT
//
*/
k2k_Signal* k2k_Signal_Create(int sigNo, k2k_Signal_CallbackFunc callback, void *customData)
{
	k2k_Signal *signal = NULL;
	signal = malloc(sizeof(k2k_Signal));
	if (signal != NULL)
	{
		/*! store reference to callback function */
		signal->callback = callback;
		/*! store reference to custom data */
		signal->customData = customData;
		/*! pid of signal receiver (current process) */
		signal->pid = getpid();
		/*! set up signal handler */
		signal->sigNo = sigNo;
		sigfillset (&signal->blockMask);
		signal->usrAction.sa_sigaction = k2k_Signal_CallbackFuncInternal;
		signal->usrAction.sa_mask = signal->blockMask;
		/*! invoke signal-catching function with three arguments instead of one */
		signal->usrAction.sa_flags = SA_SIGINFO;
		/*! signal handler is not enabled */
		signal->enabled = 0;
		/*! attach handler */ 
		if (sigaction(signal->sigNo, &signal->usrAction, &signal->oldAction) != 0)
		{
			free(signal);
			signal = NULL;
		}
	}
	return signal;
}

/*!
//
// Releases Signal ADT "object"
// 
// \param signal is the pointer to the Template structure 
// 
// \return noting
//
*/
void k2k_Signal_Release(k2k_Signal *signal)
{
	if (signal != NULL)
	{		
		/*! reattach old handler */
		sigaction(signal->sigNo, &signal->oldAction, NULL);
		/*! release memory */
		free(signal);	
	}
}

/*!
//
// Enables signal handling
// 
// \param signal is the pointer to the Signal structure 
// 
// \return noting
//
*/
void k2k_Signal_Enable(k2k_Signal *signal)
{
	if (signal->enabled == 0)
	{
		signal->enabled = 1;
	}
}

/*!
//
// Disables signal handling
// 
// \param signal is the pointer to the Signal structure 
// 
// \return noting
//
*/
void k2k_Signal_Disable(k2k_Signal *signal)
{
	if (signal->enabled != 0)
	{
		signal->enabled = 0;
	}
}

/*!
//
// Raises a signal (to be called by forked process)
// 
// \param signal is the pointer to the Signal structure 
// 
// \return noting
//
*/
void k2k_Signal_Raise(k2k_Signal *signal)
{
	union sigval sval;
	sval.sival_ptr = signal;
	sigqueue(signal->pid, signal->sigNo, sval);
}

/*!
//
// Callback function which is called when signal arrives
// 
// \param signal is the pointer to the Signal structure 
// 
// \return noting
//
*/
static void k2k_Signal_CallbackFuncInternal(int sigNo, siginfo_t *sigInfo, void *context)
{
	k2k_Signal *signal = sigInfo->si_ptr;
	if (signal != NULL && signal->sigNo == sigNo && signal->enabled != 0)
	{
		signal->callback(sigNo, signal->customData);
	}
	if (sigNo == SIGCHLD)
	{
		/*! prevent zombies */
		while (waitpid(-1, NULL, WNOHANG) > 0) 
		{
			/*! non-blocking wait for all dead processes */
			usleep(500);
		}
	}
}

